﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Application
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;

    using Mews.Mime;

    /// <summary>Builds a text representation of a given message.</summary>
    internal sealed class TextConverter : MessageVisitor
    {
        private readonly StringBuilder builder = new StringBuilder();
        private string quote;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>See <see cref="MessageVisitor.OnVisit(ApplicationBody)"/>.</summary>
        public sealed override void OnVisit(ApplicationBody visitable)
        {
            this.AddFile("Binary", visitable);
        }

        /// <summary>See <see cref="MessageVisitor.OnVisit(ImageBody)"/>.</summary>
        public sealed override void OnVisit(ImageBody visitable)
        {
            this.AddFile("Image", visitable);
        }

        /// <summary>See <see cref="MessageVisitor.OnVisit(TextBody)"/>.</summary>
        public sealed override void OnVisit(TextBody visitable)
        {
            if (IsAttachment(visitable))
            {
                this.AddFile("Text", visitable);
            }
            else
            {
                using (var reader = new StringReader(visitable.Content))
                {
                    string line;

                    while ((line = reader.ReadLine()) != null)
                    {
                        this.WriteLine(line);
                    }
                }
            }
        }

        /// <summary>See <see cref="MessageVisitor.OnVisit(MessageBody)"/>.</summary>
        public sealed override void OnVisit(MessageBody visitable)
        {
            if (IsAttachment(visitable))
            {
                this.AddFile("Message", visitable);
            }
            else
            {
                var currentQuoteDepth = this.quote.Length;
                this.SetQuotePrefixes(currentQuoteDepth + 1);

                try
                {
                    base.OnVisit(visitable);
                }
                finally
                {
                    this.SetQuotePrefixes(currentQuoteDepth);
                }
            }
        }

        /// <summary>See <see cref="MessageVisitor.OnVisit(MultipartBody)"/>.</summary>
        public sealed override void OnVisit(MultipartBody visitable)
        {
            if ((visitable.Subtype == MultipartSubtypeNames.Alternative) && (visitable.Parts.Count > 0))
            {
                // The first part of an alternative multipart should be the least "complicated"
                this.OnVisit(visitable.Parts[0]);
            }
            else
            {
                base.OnVisit(visitable);
            }
        }

        /// <summary>See <see cref="MessageVisitor.OnVisit(Message)"/>.</summary>
        public sealed override void OnVisit(Message visitable)
        {
            var date = visitable.Date;
            var author = GetFirstOrDefault(visitable.From);

            if (date.HasValue)
            {
                this.WriteLine("Date: " + date.Value.ToString("r", CultureInfo.InvariantCulture));
            }

            if (author != null)
            {
                this.WriteLine("From: " + author.ToString());
            }

            this.WriteLine();
            base.OnVisit(visitable);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static string ToText(Message message, int quoteDepth)
        {
            var visitor = new TextConverter(quoteDepth);
            message.Accept(visitor);
            return visitor.builder.ToString();
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private TextConverter(int quoteDepth)
        {
            this.SetQuotePrefixes(quoteDepth);
        }

        private void AddFile(string prefix, Body visitable)
        {
            this.WriteLine();
            string name = null;
            string disposition = "Attached";

            if (visitable.Disposition != null)
            {
                name = visitable.Disposition.FileName;

                if (visitable.Disposition.Kind == DispositionKind.Inline)
                {
                    disposition = "Inline";
                }
            }

            this.WriteLine(
                "[" + disposition + " " + prefix + " File" + (string.IsNullOrEmpty(name) ? name : " " + name) + "]");
            this.WriteLine();
        }

        private void WriteLine()
        {
            this.builder.AppendLine(this.quote);
        }

        private void WriteLine(string line)
        {
            this.builder.Append(this.quote);
            this.builder.AppendLine(line);
        }

        private void SetQuotePrefixes(int quoteDepth)
        {
            this.quote = new string('>', quoteDepth);
        }

        private static bool IsAttachment(Body body)
        {
            return (body.Disposition != null) && (body.Disposition.Kind == DispositionKind.Attachment);
        }

        private static T GetFirstOrDefault<T>(IList<T> list) where T : class
        {
            return list == null ? (T)null : list.FirstOrDefault();
        }
    }
}
